package com.mlk.engUser.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.XmlUtil;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.baomidou.mybatisplus.mapper.Condition;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mlk.constant.EngConstant;
import com.mlk.core.component.page.Page;
import com.mlk.core.component.page.Pager;
import com.mlk.core.constants.Constant;
import com.mlk.core.exception.GlobalException;
import com.mlk.core.exception.ParamException;
import com.mlk.core.util.*;
import com.mlk.core.util.validate.ValidateUtils;
import com.mlk.engSystem.constant.SystemParam;
import com.mlk.engSystem.service.EngSysParamService;
import com.mlk.engUser.excle.EngUserEXCLE;
import com.mlk.engUser.mapper.*;
import com.mlk.engUser.po.*;
import com.mlk.engUser.service.EngUserService;
import com.mlk.engUser.tonglian.AIPGException;
import com.mlk.engUser.tonglian.DemoConfig;
import com.mlk.engUser.tonglian.util.DemoUtil;
import com.mlk.engUser.tonglian.util.HttpUtil;
import com.mlk.engUser.tonglian.xml.XmlParser;
import com.mlk.engUser.tonglian.xstruct.common.AipgReq;
import com.mlk.engUser.tonglian.xstruct.common.InfoReq;
import com.mlk.engUser.tonglian.xstruct.trans.breq.Body;
import com.mlk.engUser.tonglian.xstruct.trans.breq.Trans_Detail;
import com.mlk.engUser.tonglian.xstruct.trans.breq.Trans_Sum;
import com.mlk.engUser.utils.PhoneVerifyCodeUtils;
import com.mlk.engUser.vo.EngUserRemainVO;
import com.mlk.engUser.vo.EngUserVO;
import com.mlk.wx.WxPayProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 客户管理
 * @author xulu
 */
@Service
@Slf4j
public class EngUserServiceImpl implements EngUserService {

    @Resource
    private EngUserMapper engUserMapper;

    @Resource
    private EngUserShopPicMapper engUserShopPicMapper;

    @Resource
    private EngUserShopLicencePicMapper engUserShopLicencePicMapper;

    @Resource
    private EngUserAddressMapper engUserAddressMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Resource
    private EngUserInRemainMapper engUserInRemainMapper;

    @Resource
    private EngUserRemainVerifyMapper engUserRemainVerifyMapper;

    @Resource
    private EngUserOutRemainMapper engUserOutRemainMapper;

    @Override
    @Transactional
    public ServerResponse register(EngUser user,Integer verifyCode) {

        // 验证码
        if(verifyCode == null){
            return ServerResponse.fail("没有输入验证码");
        }

        // check
        ValidateUtils.validate(user);

        String code = this.redisTemplate.opsForValue().get(EngConstant.PHONE_PREFIX_CODE + user.getPhone());
        if(code == null){
            return ServerResponse.fail("验证码已过期或错误，请重新申请");
        }

        if(! code.equals(verifyCode.toString()) ){
            return ServerResponse.fail("验证码错误");
        }

        user.setRegisteTime(new Date());
        user.setPassword(MD5Utils.encode(user.getPassword()));
        int count = this.engUserMapper.insert(user);

        if(count == 0){
            return ServerResponse.fail("注册失败");
        }

        return ServerResponse.ok("注册成功");
    }

    /**
     * 店铺照片上传
     * @param openId
     * @param files
     * @return
     */
    @Override
    public ServerResponse uploadShopPic(String openId, MultipartFile[] files,int type) {
        if(StringUtils.isEmpty(openId)){
            return ServerResponse.fail("没有携带openid");
        }

        if(ArrayUtils.isEmpty(files)){
            return ServerResponse.fail("请上传照片");
        }

        if(type == 0){
            return ServerResponse.fail("请输入type参数");
        }
        List<String> urls = Lists.newArrayList();
        // 上传店铺照片
        if(type == 1){
            Arrays.stream(files).forEach(e->{
                FileUploadUtils.UploadContext context = FileUploadUtils.uploadContext(e, EngConstant.PicPath.SHOP_PIC, UUIDUtils.uuid());
                this.engUserShopPicMapper.insert(EngUserShopPic.builder().openId(openId).address(context.getSaveFullPath()).build());
                urls.add(context.getSaveFullPath());
            });
        }else{
            // 上传执照图片
            Arrays.stream(files).forEach(e->{
                FileUploadUtils.UploadContext context = FileUploadUtils.uploadContext(e, EngConstant.PicPath.SHOP_LICENSE, UUIDUtils.uuid());
                this.engUserShopLicencePicMapper.insert(EngUserShopLicencePic.builder().openId(openId).picAddress(context.getSaveFullPath()).build());
                urls.add(context.getSaveFullPath());
            });
        }

        return ServerResponse.ok("上传成功",urls);
    }

    @Override
    public ServerResponse modifyPassword(String openId, String newPassword) {
        EngUser personInfo = this.engUserMapper.selectOne(EngUser.builder().openId(openId).build());
        return this.engUserMapper.updateById(EngUser.builder().id(personInfo.getId()).password(MD5Utils.encode(newPassword)).build())== 0 ?
                ServerResponse.fail("修改失败") :ServerResponse.ok("修改成功");
    }

    /**
     * 判断是否注册过
     * @param openId
     * @return
     */
    @Override
    public ServerResponse checkIsRegistered(String openId) {
        int count =  this.engUserMapper.selectCount(new EntityWrapper<>(EngUser.builder().openId(openId).build()));
        if(count == 0){
            return ServerResponse.fail("用户未注册");
        }
        return ServerResponse.ok("用户已注册");
    }

    /**
     * 添加地址
     *  @param engUserAddress
     * @return
     */
    @Override
    @Transactional
    public ServerResponse addAddress(EngUserAddress engUserAddress){
       resetUserAddressMainFlag(engUserAddress);

       int count = this.engUserAddressMapper.insert(engUserAddress);

       if (count == 0){
           return ServerResponse.fail("添加失败");
       }
       return ServerResponse.ok("添加成功");
    }

    /**
     * 删除地址
     *  @param id
     * @return
     */
    @Override
    @Transactional
    public ServerResponse deleteAddress(Integer id){
       int count = this.engUserAddressMapper.deleteById(id);
       if (count == 0){
           return ServerResponse.fail("删除失败");
       }
       return ServerResponse.ok("删除成功");
    }

    /**
     * 用户总收益
     * @param openId
     * @return
     */
    @Override
    public ServerResponse findTotalIncome(String openId) {
        return ServerResponse.ok(this.engUserInRemainMapper.findTotalIncome(openId));
    }

    /**
     * 用户总收益明细
     * @param openId
     * @return
     */
    @Override
    public Pager findTotalIncomeDetail(String openId) {
        List<EngUserInRemain> list = this.engUserInRemainMapper.selectList(Condition.create().eq("open_id",openId).orderBy("create_time",false));
        list.stream().forEach(e->{
            e.setName(EngConstant.IN_REMAIN_SOURCE_REF.getValue(e.getType()));
        });
        return Pager.adapt(list);
    }

    private ReentrantLock lock = new ReentrantLock();

    @Resource
    private WxPayProperties properties;

    @Resource
    private EngSysParamService engSysParamService;

    /**
     * 用户申请提现
     * @param openId
     * @param amount
     * @return
     */
    @Override
    @Transactional
    public ServerResponse applyRemain(String openId, BigDecimal amount,Integer cardId) {
        if(cardId == null){
            throw new GlobalException("请选择提现绑定的银行卡");
        }
        EngUser user = this.engUserMapper.selectOne(EngUser.builder().openId(openId).build());

        int minNum = this.engSysParamService.getIntValue(SystemParam.PAY_MIN_NUM);
        int maxNum = this.engSysParamService.getIntValue(SystemParam.PAY_MAX_NUM);

        if(minNum > amount.intValue() || maxNum < amount.intValue()){
            throw new GlobalException("用户一次提现申请额度需要在"+minNum+"到"+maxNum+"元之间");
        }

        try{
            lock.lock();
            // 判断用户是否有这些余额
            if(user.getRemain().doubleValue() < amount.doubleValue()){
                throw new GlobalException("用户账户余额不足");
            }

            // 申请单只允许存在一个,只有提现成功或失败之后才能继续提现
            int count = this.engUserRemainVerifyMapper.getCountOfOpenIdWithStatusInit(openId);
            if(count > 0){
                // 说明之前存在一个申请单了
                throw new GlobalException("存在一个未处理完成的提现申请,等处理完之后再次提现");
            }

            // 下面发起提现申请
            String sn = this.applyToTonglian(openId, amount, cardId);
            // 现在是正常情况了
            // 插入数据
            EngUserRemainVerify verify = new EngUserRemainVerify();
            verify.setCreateTime(new Date());
            verify.setOpenId(user.getOpenId());
            verify.setUserName(user.getName());
            verify.setAmount(amount);
            verify.setSn(sn);
            this.engUserRemainVerifyMapper.insert(verify);

            return ServerResponse.ok("操作成功");
        }finally {
            lock.unlock();
        }

    }

    @Resource
    private EngUserBankCardMapper engUserBankCardMapper;


    /**
     * 调用通联师傅,成功会返回req_sn代码
     * @param openId
     * @param amount
     * @param cardId
     * @return
     */
    private String applyToTonglian(String openId, BigDecimal amount,Integer cardId){

        EngUserBankCard card = this.engUserBankCardMapper.selectById(cardId);

        InfoReq inforeq = DemoUtil.makeReq("100002");
        Body reqBody = new Body();
        Trans_Sum trans_sum = new Trans_Sum();
        trans_sum.setBUSINESS_CODE("09900");//必须使用业务人员提供的业务代码，否则返回“未开通业务类型”
        trans_sum.setMERCHANT_ID(DemoConfig.merchantid) ;
        trans_sum.setTOTAL_ITEM("1") ;
        trans_sum.setTOTAL_SUM(String.valueOf((int)(amount.doubleValue() * 100))) ;
        reqBody.setTRANS_SUM(trans_sum);

        List<Trans_Detail> transList = new ArrayList<Trans_Detail>();
        Trans_Detail trans_detail = new Trans_Detail() ;
        trans_detail.setSN("1") ;
        trans_detail.setACCOUNT_NAME(card.getName()) ;
        trans_detail.setACCOUNT_PROP("0") ;
        trans_detail.setACCOUNT_NO(card.getBankCode()) ;
        //trans_detail.setBANK_CODE("0105") ;
        trans_detail.setAMOUNT(String.valueOf((int)(amount.doubleValue() * 100))) ;
        trans_detail.setCURRENCY("CNY");
        /*trans_detail.setNOTIFYURL("https://piliang1");*/
        transList.add(trans_detail) ;

        reqBody.setDetails(transList);

        AipgReq req = new AipgReq();
        req.setINFO(inforeq);
        req.addTrx(reqBody);
        try{
            //step1 对象转xml
            String xml = XmlParser.toXml(req);
            //step2 加签

            String signedXml = DemoUtil.buildSignedXml(xml.replaceAll("DTLS","TRANS_DETAILS"));
            //step3 发往通联
            String url = DemoConfig.url+"?MERCHANT_ID="+DemoConfig.merchantid+"&REQ_SN="+inforeq.getREQ_SN();
            System.out.println("============================请求报文============================");
            System.out.println(signedXml);
            String respText = HttpUtil.post(signedXml, url);
            System.out.println("============================响应报文============================");
            System.out.println(respText);
            //step4 验签
            if(!DemoUtil.verifyXml(respText)){
                log.error("提现失败,验签失败");
            }
            //step5 xml转对象
            //AipgRsp rsp = XmlParser.parseRsp(respText);
            //InfoRsp infoRsp = rsp.getINFO();
            String retCode = XmlUtil.parseXml(respText).getElementsByTagName("RET_CODE").item(0).getTextContent();
            String errMsg = XmlUtil.parseXml(respText).getElementsByTagName("ERR_MSG").item(0).getTextContent();
            if("0000".equals(retCode)){
                String reqSn = XmlUtil.parseXml(respText).getElementsByTagName("REQ_SN").item(0).getTextContent();
                return reqSn;
            }else{
                throw new GlobalException("提现出现问题,错误码{"+retCode+"},错误原因{"+errMsg+"}");
            }
        }catch(AIPGException e){
            log.error("提现失败",e);
            throw new GlobalException("提现失败"+e);
        }
    }

    /**
     * 分页查询提现申请
     * @param bean
     * @return
     */
    @Override
    @Page
    public Pager verifyList(EngUserRemainVerify bean) {
        return Pager.adapt(this.engUserRemainVerifyMapper.selectForPage(bean));
    }

    /**
     * 修改地址页面的列表展示
     *  @param
     * @return
     */
    public ServerResponse addressList(String openId){
       List<EngUserAddress> list = this.engUserAddressMapper.addressList(openId);
       return ServerResponse.ok(list);
    }

    @Override
    @Transactional
    public ServerResponse updateDefaultAddress(String openId, Integer id) {

        resetUserAddressMainFlag(EngUserAddress.builder().openId(openId).build());
        // 更新现在的记录
        return this.engUserAddressMapper.updateById(EngUserAddress.builder().id(id).mainFlag(1).build()) == 0?
                ServerResponse.fail("设置失败") :ServerResponse.ok("设置成功");
    }

    /**
     * 修改用户地址
     * @param engUserAddress
     * @return
     */
    @Override
    @Transactional
    public ServerResponse updateUserAddress(EngUserAddress engUserAddress) {
        if(engUserAddress.getMainFlag() == 1){
            resetUserAddressMainFlag(engUserAddress);
        }
        return this.engUserAddressMapper.updateById(engUserAddress) == 0?
                ServerResponse.fail("修改失败"): ServerResponse.ok("修改成功");
    }


    // ======
    @Value("${phoneVerifyCode.accessKeyId}")
    private String accessKeyId;

    @Value("${phoneVerifyCode.secret}")
    private String secret;

    /**
     * 获取手机验证码
     * @param phone
     * @return
     */
    @Override
    public ServerResponse getPhoneVerifyCode(String phone) {
        if(StringUtils.isBlank(phone)){
            throw new ParamException("手机号码不能为空");
        }
        // 生成验证码
        Integer code = PhoneVerifyCodeUtils.generate();
        // send
        if(sendMsg(phone,code)){
            // cached
            redisTemplate.opsForValue().set(EngConstant.PHONE_PREFIX_CODE + phone, code.toString(),  3, TimeUnit.MINUTES);

            return ServerResponse.ok("发送成功");
        }

        return ServerResponse.fail("发送失败");
    }

    /**
     * 将用户的地址全部设置为非默认地址
     * @param engUserAddress
     */
    private void resetUserAddressMainFlag(EngUserAddress engUserAddress){
        int total =  this.engUserAddressMapper.selectCount(new EntityWrapper<>(EngUserAddress.builder().openId(engUserAddress.getOpenId())
                .mainFlag(1).build()));

        if(total > 0){
            // 已经存在默认地址了,需要把之前的进行修改
            this.engUserAddressMapper.updateMainFlagByOpenId(engUserAddress.getOpenId());
        }
    }


    ///////////// -----------------bad code///////////////////////
    /**
     * 发送验证码
     * @param phone
     * @param code
     * @return
     */
    private boolean sendMsg(String phone,Integer code){
        DefaultProfile profile = DefaultProfile.getProfile("cn-shanghai", accessKeyId, secret);
        IAcsClient client = new DefaultAcsClient(profile);
        CommonRequest request = new CommonRequest();
        request.setMethod(MethodType.POST);
        request.setDomain("dysmsapi.aliyuncs.com");
        request.setVersion("2017-05-25");
        request.putQueryParameter("PhoneNumbers", phone);
        request.putQueryParameter("SignName", "能源宝验证码");
        request.putQueryParameter("TemplateCode", "SMS_168345132");
        request.putQueryParameter("TemplateParam", "{\"code\":\"" + code + "\"}");
        request.setAction("SendSms");
        try {
            CommonResponse response = client.getCommonResponse(request);
        } catch (Exception e) {
            log.error("send msg error ,",e);
            return false;
        }
        return true;
    }


    // bad code

    /**
     * 客户查询
     * @param engUser
     * @return
     */
    @Override
    @Page
    public Pager clientList(EngUserVO engUser) {
        return Pager.adapt(this.engUserMapper.clientList(engUser));
    }

    /**
     * 用户余额列表
     * @param vo
     * @return
     */
    @Override
    @Page
    public Pager userRemainList(EngUserRemainVO vo) {
        List<EngUserRemainVO> lists =  this.engUserMapper.userRemainList(vo);
        lists.forEach(e->{
            if(e.getAmount().doubleValue()>0){
                e.setTypeName(EngConstant.IN_REMAIN_SOURCE_REF.getValue(e.getType()));
            }else{
                e.setTypeName(EngConstant.OUT_REMAIN_TARGET_REF.getValue(e.getType()));
            }
        });
        return Pager.adapt(lists);
    }

    /**
     * 查询用户的当前积分
     * @param openId
     * @return
     */
    @Override
    public ServerResponse getUserCurrentScore(String openId) {
        int score =  this.engUserMapper.selectOne(EngUser.builder().openId(openId).build()).getScore();
        Map<String,Object> result = Maps.newHashMap();
        result.put("score",score);
        return ServerResponse.ok(result);
    }

    @Override
    public ServerResponse getUserCurrentRemain(String openId) {
        BigDecimal score =  this.engUserMapper.selectOne(EngUser.builder().openId(openId).build()).getRemain();
        Map<String,Object> result = Maps.newHashMap();
        result.put("remain",score.doubleValue());
        return ServerResponse.ok(result);
    }

    /**
     * 用户数据导出
     */
    @Override
    public void export() {
        List<EngUser> allData = this.engUserMapper.clientList(null);
        ExportParams exportParams = new ExportParams();
        exportParams.setTitle("客户信息"); // 设置标题
        exportParams.setSheetName("客户sheet"); // 设置sheet的名称.
        List<EngUserEXCLE> excleList = Lists.newArrayList();
        // 复制数据
        allData.forEach(e->{
            EngUserEXCLE excle = new EngUserEXCLE();
            BeanUtils.copyProperties(e,excle);
            excleList.add(excle);
        });
        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, EngUserEXCLE.class, excleList);
        ExcleDownUtils.download("客户数据-"+ DateUtil.format(new Date(), Constant.DATE_FORMAT)+".xls",workbook);
    }

    @Override
    public ServerResponse getDefaultAddress(String openId) {
        EngUserAddress address = this.engUserAddressMapper.selectOne(EngUserAddress.builder().openId(openId).mainFlag(1).build());
        return ServerResponse.ok(address);
    }
}
